Log errors and avoid sending too many emails
工作流概述
这是一个包含16个节点的复杂工作流,主要用于自动化处理各种任务。
工作流源代码
{
"id": "YybYYc430rmZWJPJ",
"meta": {
"instanceId": "febfa0961d1e55a48938f0337f348b73a50538aa16673607611ead85d95f662c",
"templateCredsSetupCompleted": true
},
"name": "Log errors and avoid sending too many emails",
"tags": [
{
"id": "7YoU4oTsaGGEtWJj",
"name": "sample",
"createdAt": "2025-01-31T16:41:27.407Z",
"updatedAt": "2025-01-31T16:41:27.407Z"
}
],
"nodes": [
{
"id": "0e44df4c-00d2-4545-89ae-844a590de369",
"name": "Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"position": [
-1200,
90
],
"parameters": {},
"typeVersion": 1
},
{
"id": "7101542a-5146-4917-a1f2-13686cad197e",
"name": "Insert Log",
"type": "n8n-nodes-base.postgres",
"position": [
-980,
40
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "N8Err",
"cachedResultName": "N8Err"
},
"schema": {
"__rl": true,
"mode": "name",
"value": "p1gq6ljdsam3x1m"
},
"columns": {
"value": {
"URL": "={{ $json.execution.url }}",
"json": "={{ JSON.stringify($json) }}",
"Stack": "={{ $json.execution.error.stack }}",
"title": "={{ $json.workflow.name }}",
"Message": "={{ $json.execution.error.message }}",
"LastNode": "={{ $json.execution.lastNodeExecuted }}",
"created_at": "={{ $now }}"
},
"schema": [
{
"id": "id",
"type": "number",
"display": true,
"removed": true,
"required": false,
"displayName": "id",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "created_at",
"type": "dateTime",
"display": true,
"required": false,
"displayName": "created_at",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "updated_at",
"type": "dateTime",
"display": true,
"removed": true,
"required": false,
"displayName": "updated_at",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "created_by",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "created_by",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "updated_by",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "updated_by",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "nc_order",
"type": "number",
"display": true,
"removed": true,
"required": false,
"displayName": "nc_order",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "title",
"type": "string",
"display": true,
"required": false,
"displayName": "title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "URL",
"type": "string",
"display": true,
"required": false,
"displayName": "URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Stack",
"type": "string",
"display": true,
"required": false,
"displayName": "Stack",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "json",
"type": "object",
"display": true,
"required": false,
"displayName": "json",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LastNode",
"type": "string",
"display": true,
"required": false,
"displayName": "LastNode",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"credentials": {
"postgres": {
"id": "2VsRB7eDnG0FA3z2",
"name": "Postgres Nocodb"
}
},
"typeVersion": 2.6
},
{
"id": "8fb1201c-353e-466c-8d08-fd969e6b10b1",
"name": "Count for 5 minutes",
"type": "n8n-nodes-base.postgres",
"position": [
-980,
-210
],
"parameters": {
"query": "SELECT count(*) FROM p1gq6ljdsam3x1m.\"N8Err\" where created_at >= $1;
",
"options": {
"queryReplacement": "={{ $now.minus(5, 'minutes').toString() }}"
},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "2VsRB7eDnG0FA3z2",
"name": "Postgres Nocodb"
}
},
"typeVersion": 2.6
},
{
"id": "89f836dc-8141-4c20-a758-bf7ff261a87b",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2260,
-300
],
"parameters": {
"color": 5,
"width": 820,
"height": 1140,
"content": "# Log errors and avoid sending too many emails
## Use case
Most of the time, it’s necessary to log all errors that occur. However, in some cases, a scheduled task or service consuming excessive resources might trigger a surge of errors.
To address this, we can log all errors but limit alerts to a maximum of one notification every 5 minutes.
## What this workflow does
This workflow can be configured to receive error events, or you can integrate it **before your own error-handling logic.**
If used as the **primary error handler**, note that this flow will **only add a database log entry** and take no further action. You’ll need to add your own alerts (e.g., email or push notifications). Below is an example of a notification setup I prefer to use.
At the end, there’s an **error cleanup** option. This feature is particularly useful in development environments.
If you already have an error-handling workflow, you can call this one as a **sub-workflow**. Its final steps include cleanup logic to reset the execution state and terminate the workflow.
## Setup
**Verify all Postgres nodes and credentials when using the 'Error Handling Sample'**
## How to adjust it to your needs
1) You can set this workflow as a sub-workflow within your existing error-handling setup.
2) Alternatively, you can add the \"Error Handling Sample\" at the end of this workflow, which sends email and push notifications.
Configuration Requirements:
⚠️ You must create a database table for this to work!
DDL of this sample:
create table p1gq6ljdsam3x1m.\"N8Err\"
(
id serial
primary key,
created_at timestamp,
updated_at timestamp,
created_by varchar,
updated_by varchar,
nc_order numeric,
title text,
\"URL\" text,
\"Stack\" text,
json json,
\"Message\" text,
\"LastNode\" text
);
alter table p1gq6ljdsam3x1m.\"N8Err\"
owner to postgres;
create index \"N8Err_order_idx\"
on p1gq6ljdsam3x1m.\"N8Err\" (nc_order);
by Davi Saranszky Mesquita
https://www.linkedin.com/in/mesquitadavi/"
},
"typeVersion": 1
},
{
"id": "fba7fec5-5285-46bd-9cc7-270b7dcc8c5f",
"name": "Principal E-Mail",
"type": "n8n-nodes-base.emailSend",
"onError": "continueErrorOutput",
"disabled": true,
"position": [
-980,
350
],
"webhookId": "d76d2e82-b0a8-4e35-88f9-1815d4ce6c79",
"parameters": {
"text": "={{ $(\"Error Trigger\").item.json.execution.url }}
{{ $(\"Error Trigger\").item.json.execution.lastNodeExecuted }}
{{ $(\"Error Trigger\").item.json.execution.error.message }}
{{ $(\"Error Trigger\").item.json.execution.error.stack }}",
"options": {
"appendAttribution": false
},
"subject": "=Erro - {{ $(\"Error Trigger\").item.json.workflow.name }}",
"toEmail": "davimesquita@gmail.com",
"fromEmail": "suporte@ideias.casa",
"emailFormat": "text"
},
"credentials": {
"smtp": {
"id": "0YIoKeISQNR2kxwO",
"name": "SMTP Resent"
}
},
"typeVersion": 2.1
},
{
"id": "979d0e82-42e8-450a-95b1-3c204ad61a50",
"name": "Fallback E-Mail",
"type": "n8n-nodes-base.emailSend",
"disabled": true,
"position": [
-760,
350
],
"webhookId": "d76d2e82-b0a8-4e35-88f9-1815d4ce6c79",
"parameters": {
"text": "={{ $(\"Error Trigger\").item.json.execution.url }}
{{ $(\"Error Trigger\").item.json.execution.lastNodeExecuted }}
{{ $(\"Error Trigger\").item.json.execution.error.message }}
{{ $(\"Error Trigger\").item.json.execution.error.stack }}",
"options": {
"appendAttribution": false
},
"subject": "=Erro - {{ $(\"Error Trigger\").item.json.workflow.name }}",
"toEmail": "davimesquita@gmail.com",
"fromEmail": "contato@ideias.casa",
"emailFormat": "text"
},
"credentials": {
"smtp": {
"id": "UvWloRL7Jyqt8tm9",
"name": "SMTP Contato"
}
},
"typeVersion": 2.1
},
{
"id": "6c073c03-e00e-45b1-8f14-faa29fd58472",
"name": "Push mobile notification",
"type": "n8n-nodes-base.pushover",
"disabled": true,
"position": [
-980,
550
],
"parameters": {
"message": "={{ $(\"Error Trigger\").item.json.workflow.name }} - {{ $(\"Error Trigger\").item.json.execution.url }}
{{ $(\"Error Trigger\").item.json.execution.lastNodeExecuted }}
{{ $(\"Error Trigger\").item.json.execution.error.message }}
{{ $(\"Error Trigger\").item.json.execution.error.stack }}",
"userKey": "=u4RMqXQR9EFdeSQBfaL1riBy1Qd953",
"additionalFields": {}
},
"credentials": {
"pushoverApi": {
"id": "ae8Jsj87n2hSWDbs",
"name": "Pushover account"
}
},
"typeVersion": 1
},
{
"id": "4ca939e4-dcb1-40bd-b5eb-4cd00cb403fb",
"name": "Truncate Log Database",
"type": "n8n-nodes-base.postgres",
"position": [
-980,
810
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "N8Err",
"cachedResultName": "N8Err"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "p1gq6ljdsam3x1m",
"cachedResultName": "p1gq6ljdsam3x1m"
},
"options": {},
"operation": "deleteTable",
"restartSequences": true
},
"credentials": {
"postgres": {
"id": "2VsRB7eDnG0FA3z2",
"name": "Postgres Nocodb"
}
},
"typeVersion": 2.6
},
{
"id": "1eaf67ca-fb77-4b76-8ee3-ae65d4b79182",
"name": "Sometimes... just cleanup",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1200,
810
],
"parameters": {},
"typeVersion": 1
},
{
"id": "01e5a7dd-41a2-43f1-bbf5-241e6791cf18",
"name": "Call this Sample - Prepend to your error catcher",
"type": "n8n-nodes-base.executeWorkflow",
"disabled": true,
"position": [
-1200,
450
],
"parameters": {
"options": {},
"workflowId": {
"__rl": true,
"mode": "id",
"value": ""
}
},
"typeVersion": 1.2
},
{
"id": "4386788d-5f10-468a-8a02-cff45a4a7ed5",
"name": "See below to prepend this at your error handling",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
-1200,
-260
],
"parameters": {
"inputSource": "passthrough"
},
"typeVersion": 1.1
},
{
"id": "d6aed974-4a36-4edd-809d-867a95d0f6ef",
"name": "If there is no logs in 5 minutes",
"type": "n8n-nodes-base.if",
"position": [
-760,
-210
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "a17b915d-f581-4774-a78a-48bc386aebc9",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.count }}",
"rightValue": 0
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "3c49f611-f1a6-409a-a4c6-903dadb27165",
"name": "CleanUp execution. See below if you will prepend this workflow",
"type": "n8n-nodes-base.code",
"position": [
-540,
-10
],
"parameters": {
"jsCode": "return [];"
},
"typeVersion": 2
},
{
"id": "192443fc-c032-4815-acc7-c8cf6040cc34",
"name": "Insert your error handling logic after this",
"type": "n8n-nodes-base.noOp",
"position": [
-540,
-260
],
"parameters": {},
"typeVersion": 1
},
{
"id": "2f87907f-816f-4054-8517-bb713a203131",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1350,
250
],
"parameters": {
"width": 840,
"height": 460,
"content": "# Error handling sample
"
},
"typeVersion": 1
},
{
"id": "b173898f-d1d8-4f83-b7b7-ba52cab7651e",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1610,
630
],
"parameters": {
"width": 1140,
"height": 340,
"content": "# Database Cleanup: Useful in DEV, but DO NOT run in production"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {
"Error Trigger": [
{
"json": {
"workflow": {
"id": "1",
"name": "Example Workflow"
},
"execution": {
"id": 231,
"url": "https://work.ideias.casa/execution/workflow/1/231",
"mode": "manual",
"error": {
"stack": "Stacktrace",
"message": "Example Error Message"
},
"retryOf": "34",
"lastNodeExecuted": "Node With Error"
}
}
}
]
},
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"executionOrder": "v1",
"saveManualExecutions": false,
"saveExecutionProgress": false,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "none"
},
"versionId": "07c6795a-f906-4e22-a15a-4f1984e540a3",
"connections": {
"Insert Log": {
"main": [
[
{
"node": "CleanUp execution. See below if you will prepend this workflow",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger": {
"main": [
[
{
"node": "Insert Log",
"type": "main",
"index": 0
},
{
"node": "Count for 5 minutes",
"type": "main",
"index": 0
}
]
]
},
"Principal E-Mail": {
"main": [
[],
[
{
"node": "Fallback E-Mail",
"type": "main",
"index": 0
}
]
]
},
"Count for 5 minutes": {
"main": [
[
{
"node": "If there is no logs in 5 minutes",
"type": "main",
"index": 0
}
]
]
},
"Sometimes... just cleanup": {
"main": [
[
{
"node": "Truncate Log Database",
"type": "main",
"index": 0
}
]
]
},
"If there is no logs in 5 minutes": {
"main": [
[
{
"node": "Insert your error handling logic after this",
"type": "main",
"index": 0
}
],
[
{
"node": "CleanUp execution. See below if you will prepend this workflow",
"type": "main",
"index": 0
}
]
]
},
"Call this Sample - Prepend to your error catcher": {
"main": [
[
{
"node": "Principal E-Mail",
"type": "main",
"index": 0
},
{
"node": "Push mobile notification",
"type": "main",
"index": 0
}
]
]
},
"See below to prepend this at your error handling": {
"main": [
[
{
"node": "Insert Log",
"type": "main",
"index": 0
},
{
"node": "Count for 5 minutes",
"type": "main",
"index": 0
}
]
]
},
"CleanUp execution. See below if you will prepend this workflow": {
"main": [
[]
]
}
}
}
功能特点
- 自动检测新邮件
- AI智能内容分析
- 自定义分类规则
- 批量处理能力
- 详细的处理日志
技术分析
节点类型及作用
- Errortrigger
- Postgres
- Stickynote
- Emailsend
- Pushover
复杂度评估
配置难度:
维护难度:
扩展性:
实施指南
前置条件
- 有效的Gmail账户
- n8n平台访问权限
- Google API凭证
- AI分类服务订阅
配置步骤
- 在n8n中导入工作流JSON文件
- 配置Gmail节点的认证信息
- 设置AI分类器的API密钥
- 自定义分类规则和标签映射
- 测试工作流执行
- 配置定时触发器(可选)
关键参数
| 参数名称 | 默认值 | 说明 |
|---|---|---|
| maxEmails | 50 | 单次处理的最大邮件数量 |
| confidenceThreshold | 0.8 | 分类置信度阈值 |
| autoLabel | true | 是否自动添加标签 |
最佳实践
优化建议
- 定期更新AI分类模型以提高准确性
- 根据邮件量调整处理批次大小
- 设置合理的分类置信度阈值
- 定期清理过期的分类规则
安全注意事项
- 妥善保管API密钥和认证信息
- 限制工作流的访问权限
- 定期审查处理日志
- 启用双因素认证保护Gmail账户
性能优化
- 使用增量处理减少重复工作
- 缓存频繁访问的数据
- 并行处理多个邮件分类任务
- 监控系统资源使用情况
故障排除
常见问题
邮件未被正确分类
检查AI分类器的置信度阈值设置,适当降低阈值或更新训练数据。
Gmail认证失败
确认Google API凭证有效且具有正确的权限范围,重新进行OAuth授权。
调试技巧
- 启用详细日志记录查看每个步骤的执行情况
- 使用测试邮件验证分类逻辑
- 检查网络连接和API服务状态
- 逐步执行工作流定位问题节点
错误处理
工作流包含以下错误处理机制:
- 网络超时自动重试(最多3次)
- API错误记录和告警
- 处理失败邮件的隔离机制
- 异常情况下的回滚操作